type CanvasLabelType = 'box' | 'polygon'
type CanvasLabelListItem = {
    id: number
    labelTypeIds: number[]
    type: CanvasLabelType | ''
    data: number[]
}
type CanvasEventType = "checkType" | "drawEnd" | "hover" | 'scale'| 'translate' | 'checkLabelType' | 'select' 
type CanvasEvent = {
    type: CanvasEventType
    data: any
}
type CanvasEventCbk = (event?: CanvasEvent) => void
// 操作类型
type CanvasLabelOpType = CanvasLabelType | 'none' | 'select' | 'move'
type CanvasLabelImageData = ImageBitmap | ImageData
// 背景图片
type BackData = {
    // 水平移动距离
    mx: number
    // 垂直移动距离
    my: number
    // 图片宽度
    w: number
    // 图片高度
    h: number
    // 根据图片原始大小计算的缩放比例
    zoom: number
    // 原始图片数据
    data: ImageBitmapSource
}
type LabelType = {
    name: string
    id: number
    strokeWidth: number
    strokeStyle: string
    fillStyle: string
    fontFillStyle: string
    font: string
}
type CanvasLabelSetting = {
    background?: string
    labelTypeList?: LabelType[]
    showLabelTagType?: 'number' | 'name' | 'id'
    offKbdEvent?:boolean
    sightBead?: {
        coordinatesColor?: string
        coordinatesWidth?: number
        fontColor?: string
        fontStyle?: string
    }
    selectStyle?: {
        strokeWidth?: number
        strokeStyle?: string
        fillStyle?: string
        pointFillR?: number
        pointFillStyle?: string
    }
    selectIngStyle?: {
        strokeWidth?: number
        strokeStyle?: string
        fillStyle?: string
    }
}
function deepCopy<T>(data: T): T {
    return JSON.parse(JSON.stringify(data))
}

function getLabelData(l: CanvasLabelListItem) {
    if (l.type === 'box') {
        return [
            l.data[0], l.data[1],
            l.data[0] + l.data[2], l.data[1],
            l.data[0] + l.data[2], l.data[1] + l.data[3],
            l.data[0], l.data[1] + l.data[3]]
    } else if (l.type === 'polygon') {
        return deepCopy(l.data)
    }
    return []
}
function copyBD(b:BackData){
    return {
        mx:b.mx,
        my:b.my,
        zoom:b.zoom
    }
}
class CanvasLabel {
    // 容器元素
    #el: HTMLElement | null = null
    // 2d上下文
    #ctx: CanvasRenderingContext2D | null = null
    // 画布元素
    #canvas: HTMLCanvasElement | null = null
    #resizeOB: ResizeObserver | null = null
    #backData: BackData | null = null
    #setting: CanvasLabelSetting = {}
    #store: {
        mouseIsDown: boolean
        x: number
        y: number
        cx: number
        cy: number
        oldOpType: CanvasLabelOpType
        hiddenLabelTypeIds: number[]
        hiddenLabelIds: number[]
        useLabelTypeId: number[]
    } = {
            mouseIsDown: false,
            x: 0,
            y: 0,
            cx: 0,
            cy: 0,
            oldOpType: 'none',
            hiddenLabelTypeIds: [],
            hiddenLabelIds: [],
            useLabelTypeId: [1]
        }
    // 操作类型
    #opType: CanvasLabelOpType = 'none'
    // 所有标签数据
    #labelList: CanvasLabelListItem[] = []
    #selectLabelIds: number[] = []
    // 正在标注的数据
    #labelIngData: CanvasLabelListItem | null = null
    #eventFun: CanvasEventCbk = () => { }
    constructor(el: string, watchResize?: boolean) {
        this.#el = document.querySelector(el)
        let canvas = document.createElement('canvas')
        canvas.innerHTML = '你的浏览器不支持canvas'
        canvas.style.userSelect = 'none'
        this.#ctx = canvas.getContext('2d')
        this.#canvas = canvas
        // 隐藏鼠标
        canvas.style.cursor = "none"
        this.#el?.appendChild(canvas)
        // 调整窗口大小
        if (this.#el) {
            if (watchResize) {
                let resizeob = new ResizeObserver(() => {
                    this.reSize()
                })
                this.#resizeOB = resizeob
                resizeob.observe(this.#el)
            } else {
                this.reSize()
            }
        }
        this.setSetting()
        this.#onEvent()
    }
    /**
     * 重置画布大小
     * @param w
     * @param h
     */
    reSize(w?: number, h?: number): void {
        if (this.#canvas && this.#el) {
            if (w !== undefined && h !== undefined) {
                this.#canvas.width = w
                this.#canvas.height = h
            } else {
                this.#canvas.width = this.#el.clientWidth
                this.#canvas.height = this.#el.clientHeight
            }
        }
        this.#drawPipeLine()
    }
    /**
     * 重绘
     */
    reDraw() {
        this.#drawPipeLine()
    }
    /**
     * 删除标签
     * @param id 标签id
     */
    delLabel(id: number | number[]) {
        const fun = (id: number) => {
            for (let i = 0; i < this.#labelList.length; i++) {
                if (this.#labelList[i].id === id) {
                    this.#labelList.splice(i, 1)
                }
            }
        }
        if (Array.isArray(id)) {
            id.forEach((d) => {
                fun(d)
            })
        } else if (typeof id === 'number') {
            fun(id)
        }
        this.#drawPipeLine()
        // this.#drawLocation()
        this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) })
    }
    /**
     * 添加设置
     * @param st 设置
     */
    setSetting(st?: CanvasLabelSetting) {
        this.#setting.background = st?.background || '#bcffc3'
        this.#setting.showLabelTagType = st?.showLabelTagType || 'name'
        this.#setting.labelTypeList = st?.labelTypeList || [{
            name: '标签1',
            id: 1,
            strokeWidth: 2,
            strokeStyle: 'yellow',
            fillStyle: '#0f04',
            fontFillStyle: 'yellow',
            font: "15px '微软雅黑'"
        }]
        this.#setting.sightBead = {
            coordinatesColor: st?.sightBead?.coordinatesColor || '#888',
            coordinatesWidth: st?.sightBead?.coordinatesWidth || 2,
            fontColor: st?.sightBead?.fontColor || '#ff6',
            fontStyle: st?.sightBead?.fontStyle || '10px "微软雅黑"'
        }
        this.#setting.selectStyle = {
            strokeStyle: st?.selectStyle?.strokeStyle || "rgba(200,0,0,0.8)",
            strokeWidth: st?.selectStyle?.strokeWidth || 6,
            fillStyle: st?.selectStyle?.fillStyle || "rgba(122,0,0,.4)",
            pointFillR: st?.selectStyle?.pointFillR || 6,
            pointFillStyle: st?.selectStyle?.pointFillStyle || "rgba(0,230,25,.9)"
        }
        this.#setting.selectIngStyle = {
            strokeStyle: st?.selectStyle?.strokeStyle || "rgba(100,60,0,0.8)",
            strokeWidth: st?.selectStyle?.strokeWidth || 6,
            fillStyle: st?.selectStyle?.fillStyle || "rgba(200,200,0,.4)"
        }
        this.#setting.offKbdEvent = st?.offKbdEvent||false
        this.#drawPipeLine()
    }
    /**
     * 获取设置
     * 
     */
    getSetting() {
        return this.#setting
    }
    /**
     * 使用某个标签
     * @param id 
     */
    useLabelType(id: number[]) {
        this.#store.useLabelTypeId = id
        this.#eventFun({ type: 'checkLabelType', data: deepCopy(this.#store.useLabelTypeId) })
    }
    /**
     * 
     * 获取现在正在使用的标签
     */
    getUseLabelType(): number[] {
        return deepCopy(this.#store.useLabelTypeId)
    }
    onEvent(cbk: CanvasEventCbk) {
        this.#eventFun = cbk
    }
    offEvent() {
        this.#eventFun = () => { }
    }
    /**
     * 设置需要隐藏的标签列表
     * @param id 
     */
    setHiddenLabels(ids: number[]) {
        this.#store.hiddenLabelIds = ids
        this.#drawPipeLine()
        this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) })
    }
    /**
     * 删除被隐藏的标签列表中的某个标签
     * @param id 
     */
    delHiddenLabel(id: number) {
        let index = this.#store.hiddenLabelIds.indexOf(id)
        this.#store.hiddenLabelIds.splice(index, 1)
        this.#drawPipeLine()
        this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) })
    }
    /**
     * 添加需要隐藏的标签
     * @param id 
     */
    addHiddenLabel(id: number) {
        this.#store.hiddenLabelIds.push(id)
        this.#drawPipeLine()
        this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) })
    }
    /**
     * 切换标签的 显示状态
     * @param id 
     */
    checkHiddenLabel(id: number) {
        if (this.labelIsHidden(id)) {
            this.delHiddenLabel(id)
        } else {
            this.addHiddenLabel(id)
        }
        this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) })
    }
    /**
     * 设置需要隐藏的标签类型列表
     * @param id 
     */
    setHiddenLabelTypes(ids: number[]) {
        this.#store.hiddenLabelTypeIds = ids
        this.#drawPipeLine()
        this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) })
    }
    /**
     * 删除需要已经隐藏的标签类型
     * @param id 
     */
    delHiddenLabelType(id: number) {
        let i = this.#store.hiddenLabelTypeIds.indexOf(id)
        this.#store.hiddenLabelTypeIds.splice(i, 1)
        this.#drawPipeLine()
        this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) })
    }
    /**
     * 添加需要隐藏的标签类型
     * @param id 
     */
    addHiddenLabelType(id: number) {
        this.#store.hiddenLabelTypeIds.push(id)
        this.#drawPipeLine()
        this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) })
    }
    /**
     * 切换标签类型的显示状态
     * @param id 
     */
    checkHiddenLabelType(id: number) {
        if (this.labelTypeIsHidden(id)) {
            this.delHiddenLabelType(id)
        } else {
            this.addHiddenLabelType(id)
        }
        this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) })
    }
    /**
     * 检查某个标签是否被隐藏
     * @param id 
     * @returns 
     */
    labelIsHidden(id: number) {
        return this.#store.hiddenLabelIds.indexOf(id) >= 0
    }
    /**
     * 检查某个标签类型是否被隐藏
     * @param id 
     * @returns 
     */
    labelTypeIsHidden(id: number) {
        return this.#store.hiddenLabelTypeIds.indexOf(id) >= 0
    }

    /**
     * 检查标签类型是否正在使用
     * @param id type id
     * @returns 返回 标签类型的使用状态
     */
    labelTypeIsUse(id: number) {
        return this.#store.useLabelTypeId.indexOf(id) >= 0
    }
    /**
     * 绘制管线
     */
    #drawPipeLine() {
        if (this.#ctx && this.#canvas) {
            let ctx = this.#ctx
            let cvs = this.#canvas
            ctx.save()
            this.clear()
            ctx.fillStyle = this.#setting?.background || '#fff'
            ctx.fillRect(0, 0, cvs.width, cvs.height)
            ctx.restore()
            ctx.save()
            if (this.#backData) {
                let d = this.#backData
                ctx.transform(d.zoom, 0, 0, d.zoom, d.mx, d.my)
                ctx.drawImage(d.data as unknown as CanvasImageSource, 0, 0, d.w, d.h)
                let line = this.#labelList
                line.forEach((item, index) => {
                    this.#drawLabel(item, index + 1)
                })
                let labeling = this.#labelIngData
                if (labeling) {
                    this.#drawLabel(labeling, 0)
                }
            }
            this.#drawPoint()
            ctx.restore()
        }
    }
    /* 获取绘制上下文 */
    getCtx() {
        return this.#ctx
    }
    /**
     * 清除指定区域的绘制 如果不传入值的话 就会清除整canvas
     * @param x 坐标
     * @param y 坐标
     * @param w 宽
     * @param h 长
     */
    clear(x?: number, y?: number, w?: number, h?: number): void {
        if (!this.#ctx || !this.#canvas) {
            return
        }
        if (x !== undefined && y !== undefined && w !== undefined && h !== undefined) {
            this.#ctx.clearRect(x, y, w, h)
        } else {
            this.#ctx.clearRect(0, 0, this.#canvas.width, this.#canvas.height)
        }
    }
    /**
     * 注册所有的事件
     */
    #onEvent() {
        if (!this.#canvas || !this.#ctx) {
            return
        }
        const cvs = this.#canvas
        let that = this
        function saveMouseP(e: MouseEvent) {
            if (!that.#backData) {
                return
            }
            // 鼠标坐标 
            let x = e.offsetX
            let y = e.offsetY
            that.#store.x = x
            that.#store.y = y
            let bd = that.#backData
            // 鼠标坐标 缩放过后再图片上的坐标
            that.#store.cx = (x - bd.mx) / bd.zoom
            that.#store.cy = (y - bd.my) / bd.zoom
            let eve = {
                cx: that.#store.cx,
                cy: that.#store.cy,
                x: that.#store.x,
                y: that.#store.y
            }
            that.#eventFun({ type: "hover", data: eve })
        }
        cvs.addEventListener('wheel', (e) => {
            e.preventDefault()
            if (!this.#canvas || !this.#backData) {
                return
            }
            let x = e.offsetX
            let y = e.offsetY

            if (e.altKey) {
                // 水平移动
                e.deltaY < 0 ? this.translate(20) : this.translate(-20)
            } else if (e.ctrlKey) {
                // 缩放
                e.deltaY < 0 ? this.scale(0.1, x, y) : this.scale(-0.1, x, y)
            } else {
                // 垂直移动
                e.deltaY < 0 ? this.translate(0, 20) : this.translate(0, -20)
            }
            this.#labelNeedSelect('ing')
            this.#drawPipeLine()
            this.#drawLocation()
        })
        cvs.addEventListener('mouseover', (e) => {
            saveMouseP(e)
        })
        cvs.addEventListener('mousemove', (e) => {
            saveMouseP(e)
            this.#labelNeedSelect('ing')
            this.#addDrawing('mousemove')
            this.#selectLabelPoint()
            this.#moveLabelPoint()
            this.#moveSelectLabel(e.movementX, e.movementY)
            this.#drawMove(e.movementX, e.movementY)
            this.#drawLocation()
        })
        cvs.addEventListener('mouseout', () => {
            this.#selectIngLabelIds = []
            this.#drawPipeLine()
        })
        addEventListener('keydown', this.#keyDownEvent.bind(this))
        addEventListener('keyup', this.#keyUpEvent.bind(this))
        cvs.addEventListener('mousedown', (e) => {
            saveMouseP(e)
            this.#store.mouseIsDown = !this.#store.mouseIsDown
        })
        cvs.addEventListener('click', () => {
            this.#addDrawing('click')
            this.#labelNeedSelect('entry')
            this.#drawPipeLine()
            this.#drawLocation()
        })
        /* 双击事件 主要用于 提交 闭合 多边形的路径 */
        cvs.addEventListener('dblclick', () => {
            this.#addDrawingEnd('dblclick')
        })
        /* 鼠标抬起时间 主要用于 提交 闭合 方框路径 */
        cvs.addEventListener('mouseup', (e) => {
            saveMouseP(e)
            this.#addDrawingEnd('mouseup')
            this.#store.mouseIsDown = false
            this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) })
        })
    }
    /**
     * 键盘按下事件 该事件将被绑定到document
     * @param e 键盘事件对象
     */
    #keyDownEvent(e: KeyboardEvent) {
        if (!this.#canvas) {
            return
        }
        if(this.#setting.offKbdEvent){
            return
        }
        if (e.key === 'Shift' && !e.ctrlKey && !e.altKey) {
            e.preventDefault()
            this.checkOpType('move')
        } else if (e.key === 'f' || e.key === 'F') {
            e.preventDefault()
            this.checkOpType('box')
        } else if (e.key === 'd' || e.key === 'D') {
            e.preventDefault()
            this.checkOpType('polygon')
        } else if (e.key === 'q' || e.key === 'Q') {
            e.preventDefault()
            this.checkOpType('select')
        } else if (e.key === 'a' || e.key === 'K'){
            this.checkOpType('none')
        }
        this.#drawPipeLine()
        this.#drawLocation()
    }
    /**
     * 键盘弹起事件 该事件将被绑定到document
     * @param e 键盘事件对象
     */
    #keyUpEvent(e: KeyboardEvent) {
        if (!this.#canvas) {
            return
        }
        // const cvs = this.#canvas
        if (e.key === 'Shift') {
            this.returnOpType()
            this.#drawPipeLine()
            this.#drawLocation()
        }
    }
    /**
     * 添加点数据
     * @param et 事件类型
     */
    #addDrawing(et: keyof WindowEventMap) {
        if (!this.#backData) {
            return
        }
        let x = this.#store.cx
        let y = this.#store.cy
        if (this.#store.mouseIsDown && this.#opType === 'box') {
            /* 有数据才能够进行画框 */
            if (this.#labelIngData) {
                let w = x - this.#labelIngData.data[0]
                let h = y - this.#labelIngData.data[1]
                if (this.#labelIngData.data.length === 4) {
                    this.#labelIngData.data.splice(this.#labelIngData.data.length - 2, 2, w, h)
                } else {
                    this.#labelIngData.data.push(w, h)
                }
            } else {
                let d: CanvasLabelListItem = {
                    id: new Date().getTime(),
                    /* 数据解构避免浅拷贝影响 */
                    labelTypeIds: [...this.#store.useLabelTypeId],
                    type: 'box',
                    data: []
                }
                d.data.push(x, y)
                this.#labelIngData = d
            }
        } else if (this.#opType === 'polygon') {
            if (et === 'click') {
                if (this.#labelIngData) {
                    // 如果有数据的话就添加
                    this.#labelIngData.data.push(x, y)
                } else {
                    // 没有数据则初始化数据
                    let d: CanvasLabelListItem = {
                        id: new Date().getTime(),
                        /* 数据解构避免浅拷贝影响 */
                        labelTypeIds: [...this.#store.useLabelTypeId],
                        type: 'polygon',
                        data: []
                    }
                    d.data.push(x, y)
                    this.#labelIngData = d
                }
            } else if (et === 'mousemove') {
                if (this.#labelIngData && this.#labelIngData.data.length > 2) {
                    this.#labelIngData.data.splice(this.#labelIngData.data.length - 2, 2, x, y)
                } else if (this.#labelIngData && this.#labelIngData.data.length === 2) {
                    this.#labelIngData.data.push(x, y)
                }
            }
        }
    }
    /**
     * 结束绘制
     * @param et 事件类型
     */
    #addDrawingEnd(et: keyof WindowEventMap) {
        if (this.#opType === 'box' && et === 'mouseup') {
            if (this.#labelIngData) {
                this.#labelList.push(this.#labelIngData)
            }
            this.#labelIngData = null
        }
        if (this.#opType === 'polygon' && et === 'dblclick') {
            if (this.#labelIngData) {
                this.#labelIngData.data.splice(this.#labelIngData.data.length - 4, 4)
                this.#labelList.push(this.#labelIngData)
            }
            this.#labelIngData = null
        }
    }

    /**
     * 添加标注数据
     * @param data 
     */
    addLabelData(data: CanvasLabelListItem[] | CanvasLabelListItem) {
        data = deepCopy(data)
        if (Array.isArray(data)) {
            this.#labelList.push(...data)
        } else {
            this.#labelList.push(data)
        }
        this.#drawPipeLine()
        this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) })
    }
    /**
     * 获取所有的标签
     */
    getLabelList(): CanvasLabelListItem[] {
        return deepCopy(this.#labelList)
    }
    /**
     * 设置标签数据
     * @param ls 标签数组
     */
    setLabelList(ls: CanvasLabelListItem[]) {
        if (Array.isArray(ls)) {
            let d = JSON.stringify(ls)
            this.#labelList = JSON.parse(d)
            this.#drawPipeLine()
            this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) })
        } else {
            throw new Error('setLabelList prama must be Array')
        }
    }
    /**
     * 检查现在的操作类型
     * @returns 操作类型
     */
    getOpType(): CanvasLabelOpType {
        return this.#opType
    }

    getImageInfo() {
        return deepCopy(this.#backData)
    }
    /**
     * 获取对应标签id 的信息
     * @param ids 标签id列表
     */
    #getLabelSetting(ids: number[]): LabelType[] {
        let r: LabelType[] = []
        if (this.#setting?.labelTypeList) {
            let ll = this.#setting.labelTypeList
            for (let l of ll) {
                for (let id of ids) {
                    if (id === l.id) {
                        r.push(l)
                        break
                    }
                }
                if (r.length == ids.length) {
                    break
                }
            }
        }
        return r
    }
    /**
     * 查询框 是否拥有某标签类型
     * @param lid 
     * @param tid 
     * @returns 
     */
    labelHasLabelType(lid: number, tid: number): boolean {
        for (let label of this.#labelList) {
            if (lid === label.id) {
                return label.labelTypeIds.indexOf(tid) >= 0
            }
        }
        return false
    }
    /**
     * 传入数据 自动判断数据是要添加到框的 标签类型还是移除
     * @param lid 
     * @param tid 
     */
    checkLabelLabelType(lid: number, tid: number) {
        for (let label of this.#labelList) {
            if (lid === label.id) {
                let index = label.labelTypeIds.indexOf(tid)
                if (index >= 0) {
                    label.labelTypeIds.splice(index, 1)
                    this.#drawPipeLine()
                }
                else {
                    label.labelTypeIds.push(tid)
                    this.#drawPipeLine()
                }
                this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) })
            }
        }
    }
    // 切换 操作类型
    checkOpType(type: CanvasLabelOpType) {
        if (!this.#canvas) {
            return
        }
        const cvs = this.#canvas
        if (this.#opType !== type) {
            this.#store.oldOpType = this.#opType
            if(type !== 'move'){
                this.#labelIngData = null
            }
            this.#opType = type
            this.#selectIngLabelIds = []
            this.#canEditPoint = []
            this.selectLabels([],false)
            if (this.#opType === 'move') {
                cvs.style.cursor = 'grab'
            } else {
                cvs.style.cursor = 'none'
            }
            this.#eventFun({ type: 'checkType', data: this.#opType })
        }
    }
    returnOpType() {
        if (!this.#canvas) {
            return
        }
        const cvs = this.#canvas
        this.#opType = this.#store.oldOpType
        if (this.#opType === 'move') {
            cvs.style.cursor = 'grab'
        } else {
            cvs.style.cursor = 'none'
        }
        this.#eventFun({ type: 'checkType', data: this.#opType })
    }
    /**
     * 缩放图片
     * @param s 添加倍数
     * @param x 相对于图片X轴坐标缩放
     * @param y 相对于图片Y轴坐标缩放
     * @returns 
     */
    scale(s: number, x?: number, y?: number) {
        if (!this.#backData || !this.#canvas) {
            return
        }
        let oz = this.#backData.zoom
        // 若是缩小的时候 缩放比例 已经很小 就不缩小
        if (s < 0 && oz + s < 0.1) {
            this.#drawPipeLine()
            return
        }
        if (x === undefined || y === undefined) {
            x = this.#canvas.width / 2
            y = this.#canvas.height / 2
        }
        let nz = (this.#backData.zoom += s)
        let mx = this.#backData.mx
        let my = this.#backData.my
        this.#backData.mx = mx - ((x - mx) * (nz / oz) - (x - mx))
        this.#backData.my = my - ((y - my) * (nz / oz) - (y - my))
        this.#eventFun({ type: 'scale', data:  copyBD(this.#backData)})
        this.#drawPipeLine()
    }
    /**
     * 移动图像
     * @param x 水平
     * @param y 垂直
     */
    translate(x: number = 0, y: number = 0) {
        if (!this.#backData) {
            return
        }
        this.#backData.my += y
        this.#backData.mx += x
        this.#eventFun({ type: 'translate', data: copyBD(this.#backData) })
        this.#drawPipeLine()
    }
    /**
     * 绘制背景图
     */
    drawImage(entry: ImageBitmapSource | string): Promise<null> {
        return new Promise((res, rej) => {
            this.#backData = null
            this.#labelList = []
            const setdata = (d: CanvasLabelImageData) => {
                if (!this.#canvas) {
                    return
                }
                let data = { mx: 0, my: 0, w: d.width, h: d.height, data: d, zoom: 1 }
                let hz = this.#canvas.height / data.h
                let wz = this.#canvas.width / data.w
                data.zoom = (hz < wz ? hz : wz) - 0.05
                data.mx = (this.#canvas.width - data.zoom * data.w) / 2
                data.my = (this.#canvas.height - data.zoom * data.h) / 2
                this.#backData = data
                this.clear()
                this.#drawPipeLine()
                this.#eventFun({ type: 'scale', data: copyBD(this.#backData) })
                res(null)
            }
            if (entry instanceof Image) {
                entry.onload = async () => {
                    let bm = await createImageBitmap(entry)
                    setdata(bm)
                }
                entry.onerror = () => {
                    rej()
                }
                try {
                    (async function () {
                        let bm = await createImageBitmap(entry)
                        setdata(bm)
                    })()
                } catch {
                    rej()
                }
            } else if (entry instanceof ImageBitmap || entry instanceof ImageData) {
                setdata(entry)
            } else if (typeof entry === "string") {
                let img = new Image()
                img.onload = async () => {
                    let bm = await createImageBitmap(img)
                    setdata(bm)
                }
                img.onerror = () => {
                    rej()
                }
                img.src = entry
            }
        })

    }
    /**
     * 画框
     * @param data 框数据
     * @param fs 填充样式
     * @param ss 描边样式
     */
    #drawBox(d: CanvasLabelListItem, index: number) {
        if (!this.#ctx) {
            return
        }
        let ctx = this.#ctx
        ctx.save()
        let setting = this.#getLabelSetting(d.labelTypeIds)
        if (setting.length === 0) {
            ctx.restore()
            return
        }/* 判断是否处于选择状态 */
        if (this.#selectLabelIds.indexOf(d.id) >= 0) {
            ctx.fillStyle = this.#setting.selectStyle?.fillStyle || '#0008'
            ctx.strokeStyle = this.#setting.selectStyle?.strokeStyle || '#000'
            ctx.lineWidth = this.#setting.selectStyle?.strokeWidth || 6
        } else if (this.#selectIngLabelIds.indexOf(d.id) >= 0) {
            ctx.fillStyle = this.#setting.selectIngStyle?.fillStyle || '#0008'
            ctx.strokeStyle = this.#setting.selectIngStyle?.strokeStyle || '#000'
            ctx.lineWidth = this.#setting.selectIngStyle?.strokeWidth || 6
        } else {
            ctx.fillStyle = setting?.[0].fillStyle || '#0008'
            ctx.strokeStyle = setting?.[0].strokeStyle || '#000'
            ctx.lineWidth = setting?.[0].strokeWidth || 2
        }
        ctx.lineCap = 'round'
        ctx.lineJoin = "round"
        ctx.fillRect(d.data[0], d.data[1], d.data[2], d.data[3])
        ctx.strokeRect(d.data[0], d.data[1], d.data[2], d.data[3])
        ctx.fillStyle = setting?.[0].fontFillStyle || '#0008'
        ctx.font = setting?.[0].font || "15px '微软雅黑'"
        ctx.textAlign = 'center'
        let tag: string = ''
        if (this.#setting.showLabelTagType === 'name') {
            setting.forEach((st) => {
                tag += st.name + " "
            })
        } else if (this.#setting.showLabelTagType === 'number') {
            tag = index.toString()
        } else if (this.#setting.showLabelTagType === 'id') {
            tag = d.id?.toString() || '无id'
        }
        tag = tag.trim()
        ctx.fillText(tag || '未定义标签', (d.data[0] + d.data[0] + d.data[2]) / 2, d.data[1] - 5)
        ctx.restore()
        if (this.#selectLabelIds.indexOf(d.id) >= 0){
            let  l = getLabelData(d)
            for(let i = 0;i<l.length/2;i++){
                this.#dpi(ctx,l[i*2],l[(i*2)+1],(this.#setting.selectIngStyle?.strokeWidth||6)/2)
            }
        }
    }
    /**
     * 绘制路径点不填充渲染
     * @param ctx 2d 上下文
     * @param d 点数据
     */
    #dp(ctx: CanvasRenderingContext2D, d: CanvasLabelListItem) {
        ctx.beginPath()
        let l = d.data.length / 2
        let minx = 0
        let miny = 0
        let maxx = 0
        for (let i = 0; i < l; i++) {
            if (i == 0) {
                ctx.moveTo(d.data[0], d.data[1])
                maxx = minx = d.data[0]
                miny = d.data[1]
            } else {
                let ai = i * 2
                if (d.data[ai] < minx) {
                    minx = d.data[ai]
                } else if (d.data[ai] > maxx) {
                    maxx = d.data[ai]
                }
                if (d.data[ai + 1] < miny) {
                    miny = d.data[ai + 1]
                }
                ctx.lineTo(d.data[ai], d.data[ai + 1])
            }
        }
        ctx.closePath()
        return { minx, miny, maxx }
    }
    /**
     * 
     * @param d 绘制参数
     * @param index 
     * @returns 
     */
    #drawPath(d: CanvasLabelListItem, index: number) {
        if (!this.#ctx) {
            return
        }
        let ctx = this.#ctx
        ctx.save()
        let st = this.#getLabelSetting(d.labelTypeIds)
        if (st.length === 0) {
            ctx.restore()
            return
        }
        // 绘制路径不渲染
        let { minx, miny, maxx } = this.#dp(ctx, d)
        // 加载设置
        if (this.#selectLabelIds.indexOf(d.id) >= 0) {
            ctx.fillStyle = this.#setting.selectStyle?.fillStyle || '#0008'
            ctx.strokeStyle = this.#setting.selectStyle?.strokeStyle || '#000'
            ctx.lineWidth = this.#setting.selectStyle?.strokeWidth || 6
        } else if (this.#selectIngLabelIds.indexOf(d.id) >= 0) {
            ctx.fillStyle = this.#setting.selectIngStyle?.fillStyle || '#0008'
            ctx.strokeStyle = this.#setting.selectIngStyle?.strokeStyle || '#000'
            ctx.lineWidth = this.#setting.selectIngStyle?.strokeWidth || 6
        } else {
            ctx.fillStyle = st?.[0].fillStyle || '#0008'
            ctx.strokeStyle = st?.[0].strokeStyle || '#000'
            ctx.lineWidth = st?.[0].strokeWidth || 2
        }
        ctx.lineCap = 'round'
        ctx.lineJoin = "round"
        ctx.stroke()
        ctx.fill()
        ctx.fillStyle = st?.[0].fontFillStyle || '#0008'
        ctx.font = st?.[0].font || "15px '微软雅黑'"
        ctx.textAlign = 'center'
        let tag: string = ""
        if (this.#setting.showLabelTagType === 'name') {
            st.forEach((s) => {
                tag += s.name + " "
            })
        } else if (this.#setting.showLabelTagType === 'number') {
            tag = index.toString()
        } else if (this.#setting.showLabelTagType === 'id') {
            tag = d.id?.toString() || '无id'
        }
        tag = tag.trim()
        ctx.fillText(tag || '未定义标签', (maxx + minx) / 2, miny - 5)
        ctx.restore()
        if (this.#selectLabelIds.indexOf(d.id) >= 0){
            let  l = getLabelData(d)
            for(let i = 0;i<l.length/2;i++){
                this.#dpi(ctx,l[i*2],l[(i*2)+1],(this.#setting.selectIngStyle?.strokeWidth||6)/2)
            }
        }
    }
    /**
     * 绘制点位
     * @returns 
     */
    #dpi(ctx:CanvasRenderingContext2D,x:number,y:number,r:number){
        ctx.save()
        ctx.beginPath();
        ctx.arc(x, y, r, 0, 2 * Math.PI)
        ctx.fillStyle = this.#setting.selectStyle?.pointFillStyle || 'black'
        ctx.fill()
        ctx.closePath()
        ctx.restore()
    }
    /**
     * 
     * 绘制编辑点位  
     */
    #drawPoint() {
        if (!this.#ctx || !this.#backData) {
            return
        }
        if (this.#canEditPoint.length === 0) {
            return
        }
        let d = this.#backData
        let ctx = this.#ctx
        let r = (this.#setting.selectStyle?.pointFillR || 6) / d.zoom
        if (r < (this.#setting.selectStyle?.pointFillR || 6)) {
            r = this.#setting.selectStyle?.pointFillR || 6
        }
        let x = this.#canEditPoint[1]
        let y = this.#canEditPoint[2]
        this.#dpi(ctx,x,y,r)
    }
    /**
     * 绘制十字准星
     * @param param
     */
    #drawLocation() {
        if (!this.#canvas || !this.#ctx || this.#opType === 'move') {
            return
        }
        let x = this.#store.x
        let y = this.#store.y
        let cx = this.#store.cx
        let cy = this.#store.cy
        const cvs = this.#canvas
        const ctx = this.#ctx
        ctx.save()
        ctx.lineWidth = this.#setting?.sightBead?.coordinatesWidth || 2
        ctx.strokeStyle = this.#setting?.sightBead?.coordinatesColor || '#fff7'
        ctx.beginPath()
        ctx.moveTo(x, 0)
        ctx.lineTo(x, cvs.height)
        ctx.closePath()
        ctx.moveTo(0, y)
        ctx.lineTo(cvs.width, y)
        ctx.globalCompositeOperation = 'difference'
        ctx.stroke()
        ctx.restore()
        ctx.save()
        ctx.font = this.#setting?.sightBead?.fontStyle || '10 "微软雅黑"'
        ctx.fillStyle = this.#setting?.sightBead?.fontColor || '#fff'
        ctx.textAlign = 'center'
        ctx.globalCompositeOperation = 'difference'
        // 绘制坐标文本
        ctx.fillText(`x = ${cx.toFixed(2)}px , y = ${cy.toFixed(2)}px`, x - 100, y - 8)
        ctx.fillText(`x = ${cx.toFixed(2)}px , y = ${cy.toFixed(2)}px`, x + 100, y - 8)
        ctx.restore()
    }
    /**
     * 移动画布
     * @param x
     * @param y
     */
    #drawMove(x: number, y: number) {
        if (this.#opType !== 'move') {
            this.#drawPipeLine()
            return
        }
        this.translate(x, y)
    }
    /**
     * 
     * @param l 绘画数据
     * @param index 数据的index
     * @returns 
     */
    #drawLabel(l: CanvasLabelListItem, index: number) {
        if (this.#labelNeedHidden(l)) {
            return
        }
        if (l.type === 'box') {
            this.#drawBox(l, index)
        } else if (l.type === 'polygon') {
            this.#drawPath(l, index)
        }
    }
    /**
     * 检查标签需不需要被隐藏
     * @param l 标签信息
     * @returns 
     */
    #labelNeedHidden(l: CanvasLabelListItem) {
        if (this.labelIsHidden(l.id)) {
            return true
        }
        for (let id of l.labelTypeIds) {
            if (this.labelTypeIsHidden(id)) {
                return true
            }
        }
        return false
    }

    #selectIngLabelIds: number[] = []
    /**
     * 检查路径是否被选择 并且将被选择的id 放入到 选择列表
     * @returns 
     */
    #labelNeedSelect(et: 'entry' | 'ing') {
        if (this.#opType !== "select" || !this.#ctx || !this.#backData) {
            return
        }
        // 检查现在是否在编辑点 如果是的话 就不用进行选择
        if (this.#canEditPoint.length > 0) {
            return
        }
        const ls = this.#labelList
        let ctx = this.#ctx
        ctx.save()
        let d = this.#backData
        ctx.transform(d.zoom, 0, 0, d.zoom, d.mx, d.my)
        this.#selectIngLabelIds = []
        for (let i = ls.length - 1; i >= 0; i--) {
            ctx.save()
            if (this.#labelNeedHidden(ls[i])) {
                ctx.restore()
                continue
            }
            if (ls[i].type === 'box') {
                // 绘制矩形 不渲染
                ctx.rect(ls[i].data[0], ls[i].data[1], ls[i].data[2], ls[i].data[3])
            } else if (ls[i].type === 'polygon') {
                // 绘制路径不渲染
                this.#dp(ctx, ls[i])
            }
            // 检查坐标是否在路径内
            if (ctx.isPointInPath(this.#store.x, this.#store.y)) {
                if (et === 'ing') {
                    this.#selectIngLabelIds = [ls[i].id]
                } else if (et === 'entry') {
                    this.selectLabels([ls[i].id],false)
                }
                ctx.restore()
                ctx.restore()
                return
            }
            ctx.restore()
        }
        ctx.restore()
    }
    /**
     * 选择某些标签
     * @param ids 需要选中的标签
     */
    selectLabels(ids: number[],d = true) {
        this.#selectLabelIds = [...ids]
        this.#eventFun({ type: 'select', data: deepCopy(this.#selectLabelIds) })
        if(d){
            this.#drawPipeLine()
        }
    }

    /**
     * 获取被选择的所有标签ids
     * @returns 所有被选择的id
     */
    getSelectLabelsIds() {
        return deepCopy(this.#selectLabelIds)
    }
    /**
     * 查询标签是否现在处于被选择状态
     * @param id 标签id
     * @returns 
     */
    labelIsSelect(id: number) {
        return this.#selectLabelIds.indexOf(id) >= 0
    }
    // 可以编辑的点
    #canEditPoint: number[] = []
    // 检测鼠标位置是否在某个周围
    #atPointRound(ctx: CanvasRenderingContext2D, l: CanvasLabelListItem, d: BackData, draw = false) {
        //  将标签数据取出转换为number[]
        let p: number[] = getLabelData(l)
        let r = (this.#setting.selectStyle?.pointFillR || 6) / d.zoom
        if (r < (this.#setting.selectStyle?.pointFillR || 6)) {
            r = this.#setting.selectStyle?.pointFillR || 6
        }
        for (let i = 0; i < p.length / 2; i++) {
            ctx.save()
            ctx.beginPath();
            ctx.arc(p[i * 2], p[(i * 2) + 1], r, 0, 2 * Math.PI)
            ctx.closePath()
            if (ctx.isPointInPath(this.#store.x, this.#store.y)) {
                this.#canEditPoint = [i, p[i * 2], p[i * 2 + 1]]
                this.#selectIngLabelIds = []
                ctx.restore()
                return i
            }
            ctx.restore()
        }
        return false
    }
    /**
     * 选择某个点
     * @returns 
     */
    #selectLabelPoint() {
        if (this.#opType !== "select" || !this.#ctx || !this.#backData) {
            return
        }
        // 检查现在是否处于多选状态 或无选择 是的话就不能进行点位选择
        if (this.#selectLabelIds.length > 1 || this.#selectLabelIds.length == 0) {
            return
        }
        if (this.#store.mouseIsDown) {
            return
        }
        // 重置当前选择的点
        this.#canEditPoint = []
        for (let l of this.#labelList) {
            if (this.#selectLabelIds[0] === l.id) {
                if (this.#labelNeedHidden(l)) {
                    return
                }
                let ctx = this.#ctx
                ctx.save()
                let d = this.#backData
                ctx.transform(d.zoom, 0, 0, d.zoom, d.mx, d.my)
                this.#atPointRound(ctx, l, d)
                ctx.restore()
                return
            }
        }
    }
    /**
     * 原生移动标签坐标
     * @param x 
     * @param y 
     * @returns 
     */
    #moveSelectLabel(x: number, y: number) {
        if (this.#opType !== "select" || !this.#ctx || !this.#backData) {
            return
        }
        // 检查现在是否是在移动点位 有没有被选择的框
        if (!this.#store.mouseIsDown || this.#selectLabelIds.length === 0 || this.#canEditPoint.length > 0) {
            return
        }
        let b = this.#backData
        x = x / b.zoom
        y = y / b.zoom
        this.moveSelectLabels(x, y,false)
    }
    /**
     * 移动某个标签的某个点
     * @returns 
     */
    #moveLabelPoint() {
        if (this.#opType !== "select" || !this.#ctx || !this.#backData) {
            return
        }
        if (!this.#store.mouseIsDown) {
            return
        }
        if (this.#selectLabelIds.length == 1 && this.#canEditPoint.length > 0) {
            this.#labelList.forEach((l) => {
                if (l.id === this.#selectLabelIds[0]) {
                    let cx = this.#store.cx
                    let cy = this.#store.cy
                    if (l.type === 'box') {
                        let x = l.data[0]
                        let y = l.data[1]
                        let w = l.data[2]
                        let h = l.data[3]
                        // 根据不同点位调整元数据坐标
                        switch (this.#canEditPoint[0]) {
                            case 0:
                                l.data[0] = cx
                                l.data[1] = cy
                                l.data[2] = (x + w) - cx
                                l.data[3] = (y + h) - cy
                                break
                            case 1:
                                l.data[0] = x
                                l.data[1] = cy
                                l.data[2] = cx - x
                                l.data[3] = (y - cy) + h
                                break
                            case 2:
                                l.data[0] = x
                                l.data[1] = y
                                l.data[2] = cx - x
                                l.data[3] = cy - y
                                break
                            case 3:
                                l.data[0] = cx
                                l.data[1] = y
                                l.data[2] = (x - cx) + w
                                l.data[3] = cy - y
                                break
                        }
                    } else if (l.type === 'polygon') {
                        l.data[(this.#canEditPoint[0] * 2)] = cx
                        l.data[(this.#canEditPoint[0] * 2) + 1] = cy
                    }
                    this.#canEditPoint[1] = cx
                    this.#canEditPoint[2] = cy
                }
            })
        }
    }
    /**
     * 移动某些标签
     * @param x x 方向移动多少
     * @param y y 方向移动多少
     */
    moveSelectLabels(x: number = 0, y: number = 0,o=true) {
        const sLs = this.#selectLabelIds
        this.#labelList.forEach((l) => {
            if (sLs.indexOf(l.id) >= 0) {
                if (l.type === "box") {
                    l.data[0] = l.data[0] + x
                    l.data[1] = l.data[1] + y
                } else if (l.type === 'polygon') {
                    let data: number[] = []
                    l.data.forEach((d, i) => {
                        if ((i & 1) == 0) {
                            data.push(d + x)
                        } else {
                            data.push(d + y)
                        }
                    })
                    l.data = data
                }
            }
        })
        if(o){
            this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList)})
        }
    }
    /**
     * 销毁 移除事件绑定
     */
    des() {
        removeEventListener('keydown', this.#keyDownEvent)
        removeEventListener('keyup', this.#keyUpEvent)
        this.#resizeOB?.unobserve(this.#el as Element)
        this.#resizeOB?.disconnect()
    }
}

export { CanvasLabel, deepCopy }
export type {
    CanvasLabelType,
    CanvasLabelListItem,
    CanvasLabelOpType,
    CanvasLabelImageData,
    BackData,
    LabelType,
    CanvasLabelSetting,
    CanvasEvent,
    CanvasEventType,
    CanvasEventCbk
}

